Replace broken --headless device flow with setup instructions#34
Merged
Conversation
Fixes #31. The device code flow had multiple issues: 1. No HTTP status code checking - errors were silently ignored 2. No error field checking in device response - if Google returned an error, we'd get empty URL/code and ExpiresIn=0, causing immediate "authorization timed out" 3. Hardcoded Scopes instead of m.config.Scopes - device flow ignored the manager's configured scopes Now provides clear error messages, including specific guidance when the OAuth client isn't configured for device flow (requires "TVs and Limited Input devices" type in Google Cloud Console). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend required field validation to prevent empty device_code (which would cause polling failures) and expires_in <= 0 (which would cause immediate timeout). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Google's "TVs and Limited Input devices" OAuth clients don't include redirect_uris in their client secrets JSON, causing google.ConfigFromJSON to fail. Now we fall back to manual parsing for these clients. This allows users to use --headless with TV-type OAuth credentials without needing to also maintain Desktop app credentials. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Validate that auth_uri and token_uri are present in fallback parsing - Remove deprecated urn:ietf:wg:oauth:2.0:oob redirect URL (unused for device flow anyway) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Google's device flow only supports the full mail.google.com scope, not granular gmail.readonly/gmail.modify scopes. This causes "invalid_scope" errors when trying to use device flow with standard scopes. - Add ScopesDeviceFlow constant with mail.google.com scope - Use ScopesDeviceFlow in device flow instead of manager's configured scopes - Update saveToken to accept scopes parameter - Preserve original scopes when refreshing tokens Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Google's OAuth device code flow does not support Gmail scopes - only OpenID Connect, Drive, and YouTube are allowed. Rather than keeping broken code, `--headless` now prints instructions for the workaround: authorize on a machine with a browser and copy the token file. Changes: - Remove deviceFlow() and pollForToken() from oauth.go - Remove ScopesDeviceFlow constant - Add PrintHeadlessInstructions() to display setup steps - Simplify Authorize() to browser flow only (remove headless param) - Update addaccount.go to call instructions function for --headless The token file is portable and auto-refreshes, so the copy-token approach works reliably for headless servers. Fixes #31 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After copying the token file, users need to run `add-account` again on the headless server to register the account in the database. Changes: - Add Step 3 to headless instructions - Fix add-account to create source record when token already exists (needed for headless workflow where token was copied) - Update --headless flag help text Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Address review feedback: headless instructions now use the configured tokens directory instead of hardcoding ~/.msgvault/tokens/. This ensures correct paths for users with MSGVAULT_HOME or custom data_dir. - Extract sanitizeEmail() function for reuse - Pass tokensDir to PrintHeadlessInstructions() - Add test for sanitizeEmail() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wrap paths in single quotes to handle spaces and shell-sensitive characters in token directory paths. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add shellQuote() that handles embedded single quotes using the standard POSIX technique: ' -> '\'' This ensures the scp command works even if the token path contains single quotes (unlikely but possible). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
TV/device clients don't work with browser auth flow (no redirect_uris). Now parseClientSecrets detects this and returns a helpful error message instead of failing later at runtime. Added tests for parseClientSecrets covering valid desktop client, TV/device client rejection, and malformed JSON. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tokens directory must exist before scp can copy the token file. Add ssh mkdir -p command to step 2. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend parseClientSecrets to detect missing redirect_uris in both installed and web client types, not just installed. Added tests for valid web client and web client without redirect_uris. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Owner
Author
|
This is working now and I have validated it locally |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #31.
Problem
The
--headlessflag attempted to use Google's OAuth device flow, but this was fundamentally broken: Google's device flow does not support Gmail scopes - only OpenID Connect, Drive, and YouTube are allowed.See: https://developers.google.com/identity/protocols/oauth2/limited-input-device#allowedscopes
Solution
Remove the broken device flow code and replace
--headlesswith clear setup instructions:The token file is portable and auto-refreshes, so this approach works reliably.
Changes
deviceFlow()andpollForToken()(~130 lines of broken code)ScopesDeviceFlowconstantPrintHeadlessInstructions(email, tokensDir)with 3-step guidetokensDir(respectsMSGVAULT_HOME/ customdata_dir)shellQuote()for paths with spaces/special charsparseClientSecretsrejects TV/device clients (missingredirect_uris) with clear erroradd-accountcreates source record when token already exists (enables step 3)--headlessflag help textTesting
TestShellQuotefor shell escapingTestSanitizeEmailfor filename sanitizationTestParseClientSecretscovering valid desktop/web clients, TV/device rejection, and malformed JSON🤖 Generated with Claude Code